/*
 * Copyright (c) 2016.
 * Private license for beyondts.
 * Contact beyondts for using, beyondts999@163.com
 */

/**
 * Created by blazecrystal on 2016/12/1.
 */

/**
 * method to invoke a js method dynamically.
 * @param fn function object to invoe
 * @param args parameters for this function
 */
function invoke(fn, args) {
    fn.apply(this, args);
}

/**
 * HTTP request method.
 * @type {string} method
 */
var REQUEST_METHOD_GET = "GET", REQUEST_METHOD_POST = "POST", REQUEST_METHOD_PUT = "PUT", REQUEST_METHOD_DELETE = "DELETE";

/**
 * send a delete request using async method.
 * @param url URL of this request
 * @param params data sent to server
 * @param successCallback callback function when request successfully
 * @param errorCallback callback function when request failed
 */
function ajaxDelete(url, params, successCallback, errorCallback) {
    params._method = REQUEST_METHOD_DELETE;
    $.ajax({
        url: url,
        type: REQUEST_METHOD_POST,
        data: params,
        success: successCallback,
        error: errorCallback
    });
}

/**
 * open a modal dialog.
 * @param options a json object of options, attrs of options:
 *      backdrop: whether to show a background mask. choosable value: true/false/static, default: true
 *      keyboard: maps keyboard "esc" to modal close action. choosable value: true/false, default: true
 *      show: shows the modal when initialized. choosable value: true/false, default: true
 *      animate: whether to show/hide modal with an animate. choosable value: true/false, default: true
 *      id: id of the modal. no default value, if not set manually, it will be current time in milliseconds
 *      title: title of the modal. no default value. this can be jquery object/html/text
 *      content: content of the modal. no default value. this can be jquery object/html/text
 *      url: url of the modal's content, which will be loaded before shown using jquery "load" method. conflict with attr "content",
 *              if status for "url" load event is not "success" or "notmodified", and content attr is set, content will be shown
 *      data: data to be sent to "url". a json object.
 *      afterLoaded: function to be invoked after "url" loaded, parameters for this function will be response, status, xhr
 *      buttons: buttons for the modal. an array of jquery button object.
 *      whenShow: function to be invoked when "show" mothod invoked
 *      afterShown: function to be invoked after modal shown
 *      whenHide: function to be invoked when "hide" method invoked
 *      afterHidden: function to be invoked after modal hidden
 */
function openModal(options) {
    var DEFAULT_OPTIONS = {
        backdrop: true,
        keyboard: true,
        show: true,
        animate: true
    };
    initOptions();
    createModal();
    return options.id;
    function initOptions() {
        if (options.backdrop == null || String(options.backdrop).trim().length < 1) {
            options.backdrop = DEFAULT_OPTIONS.backdrop;
        }
        if (options.keyboard == null) {
            options.keyboard = DEFAULT_OPTIONS.keyboard;
        }
        if (options.show == null) {
            options.show = DEFAULT_OPTIONS.show;
        }
        if (options.animate == null) {
            options.animate = DEFAULT_OPTIONS.animate;
        }
        if (options.id == null || options.id.trim().length < 1) {
            options.id = new Date().getTime();
        }
    }
    function createModal() {
        var modal = $("<div/>").attr("id", options.id).addClass("modal").attr("tabindex", "-1").attr("role", "dialog").attr("aria-labelledby", "modal_title_" + options.id).attr("aria-hidden", "true");
        if (options.animate) {
            modal.addClass("fade");
        }
        modal.attr("data-backdrop", options.backdrop).attr("data-keyboard", options.keyboard).attr("data-show", options.show);
        var header = $("<div/>").addClass("modal-header").append($("<button/>").attr("type", "button").addClass("close").attr("data-dismiss", "modal").append($("<span/>").attr("aria-hidden", "true").append("&times;")).append($("<span/>").addClass("sr-only").append("关闭"))).append($("<h4/>").addClass("modal-title").attr("id", "modal_title_" + options.id).append(options.title));
        var body = $("<div/>").addClass("modal-body");
        if (options.url != null && options.url.length > 0) {
            body.load(options.url, options.data, function(resp, status, xhr) {
                if ("success" != status || "notmodified" != status) {
                    if (options.content != null && options.content.length > 0) {
                        body.append(options.content);
                    }
                }
            });
        } else if (options.content != null && options.content.length > 0) {
            body.append(options.content);
        }
        var footer = "";
        if (options.buttons != null && options.buttons.length > 0) {
            footer = $("<div/>").addClass("modal-footer");
            for (var i = 0; i < options.buttons.length; i++) {
                footer.append(options.buttons[i]);
            }
        }
        modal.append($("<div/>").addClass("modal-dialog").append($("<div/>").addClass("modal-content").append(header).append(body).append(footer))).on("hidden.bs.modal", function(ev) {
            if (options.afterHidden != null) {
                invoke(options.afterHidden, [ev]);
            }
            $("#" + options.id).remove();
        });
        if (options.whenShow != null) {
            modal.on("show.bs.modal", function(ev) {
                invoke(options.whenShow, [ev]);
            });
        }
        if (options.afterShown != null) {
            modal.on("shown.bs.modal", function(ev) {
                invoke(options.afterShown, [ev]);
            });
        }
        if (options.whenHide != null) {
            modal.on("hide.bs.modal", function(ev) {
                invoke(options.whenHide, [ev]);
            });
        }
        $("body").append(modal);
        $("#" + options.id).modal("show");
    }
}

/**
 * show a static modal for an error.
 * @param title title for this modal, jquery object/html/text
 * @param msg error message for this modal, jquery object/html/text
 * @param stack stack for this error, string array/jquery object/html/text
 * @param callback a function to invoke when button clicked
 * @param btnClass button class name of bootstram, such as btn-default/btn-primary, default is btn-default
 * @param btnLabel button label, default is "确定", value can be html/text
 */
function showError(title, msg, stack, callback, btnClass, btnLabel) {
    _showAlert("<span class=\"fa fa-exclamation-circle\" />&nbsp;&nbsp;" + title, msg, stack, callback, btnClass, btnLabel);
}

/**
 * show a static modal for a message.
 * @param title title for this modal, jquery object/html/text
 * @param msg message for this modal, jquery object/html/text
 * @param stack stack for this message, string array/jquery object/html/text
 * @param callback a function to invoke when button clicked
 * @param btnClass button class name of bootstram, such as btn-default/btn-primary, default is btn-default
 * @param btnLabel button label, default is "确定", value can be html/text
 */
function showMsg(title, msg, stack, callback, btnClass, btnLabel) {
    _showAlert("<span class=\"fa fa-info-circle\" />&nbsp;&nbsp;" + title, msg, stack, callback, btnClass, btnLabel)
}
function _showAlert(title, msg, stack, callback, btnClass, btnLabel) {
    if (btnClass == null || btnClass.length < 1) {
        btnClass = "btn-default";
    }
    if (btnLabel == null || btnLabel.length < 1) {
        btnLabel = "确定";
    }
    var btn = $("<button/>").addClass("btn").addClass(btnClass).attr("type", "button").attr("data-dismiss", "modal").append(btnLabel);
    if (callback != null) {
        btn.click(function(ev) {
            invoke(callback, [ev]);
        });
    }
    var tmp = title instanceof jQuery ? title[0].outerHTML : title;
    var title2 = tmp;
    var stack2 = null;
    if (stack != null && stack.length  > 0) {
        if ($.isArray(stack)) {
            stack2 = "";
            for (var i = 0; i < stack.length; i++) {
                stack2 += stack[i] + "<br/>";
            }
        } else {
            stack2 = stack;
        }
        if (stack instanceof jQuery) {
            stack2 = stack[0].outerHTML;
        }
        stack2 = stack2.replace("\n", "<br/>");
        tmp = $("<div/>").append($("<button/>").attr("type", "button").attr("for", "showStack").addClass("btn").addClass("btn-link").append("点击查看详细&nbsp;").append($("<i/>").addClass("fa").addClass("fa-chevron-up"))).append($("<p/>").css("display", "none").append(stack2));
    }
    var msg2 = (msg instanceof jQuery ? msg[0].outerHTML : msg) + tmp[0].outerHTML;
    var id = openModal({
        backdrop: "static",
        title: title2,
        content: msg2,
        buttons: [btn]
    });
    $("#" + id).find("[for='showStack']").click(function(ev) {
        _toggleStack($(ev.target));
    });
}
function _toggleStack(emt) {
    var currentState = $(emt).siblings("p").css("display");
    if (currentState == "none") {
        $(emt).children("i").removeClass("fa-chevron-up").addClass("fa-chevron-down");
        $(emt).siblings("p").css("display", "");
    } else {
        $(emt).children("i").removeClass("fa-chevron-down").addClass("fa-chevron-up");
        $(emt).siblings("p").css("display", "none");
    }
}

/**
 * show a static modal for a error.
 * @param title title for this modal, jquery object/html/text
 * @param msg error message for this modal, jquery object/html/text
 * @param confirmCallback a function to invoke when confirm button clicked
 * @param cancelCallback a function to invoke when cancel button clicked
 * @param confirmClass button class name of bootstram, such as btn-default/btn-primary, default is btn-default
 * @param confirmLabel confirm button label, default is "确定", value can be html/text
 * @param cancelLabel cancel button label, default is "取消", value can be html/text
 */
function showConfirm(title, msg, confirmCallback, cancelCallback, confirmClass, confirmLabel, cancelLabel) {
    if (title instanceof jQuery) {
        title = "<span class=\"fa fa-question-circle\"></span>&nbsp;&nbsp;" + title[0].outerHTML;
    } else {
        title = "<span class=\"fa fa-question-circle\"></span>&nbsp;&nbsp;" + title;
    }
    if (msg instanceof jQuery) {
        msg = msg[0].outerHTML;
    }
    if (confirmClass == null || confirmClass.length < 1) {
        confirmClass = "btn-default";
    }
    if (confirmLabel == null || confirmLabel.length < 1) {
        confirmLabel = "确定";
    }
    if (cancelLabel == null || cancelLabel.length < 1) {
        cancelLabel = "取消";
    }
    var btnCancel = $("<button/>").attr("type", "button").addClass("btn").addClass("btn-link").attr("data-dismiss", "modal").append(cancelLabel);
    if (cancelCallback != null) {
        btnCancel.click(function(ev) {
            invoke(cancelCallback, [ev]);
        });
    }
    var btnConfirm = $("<button/>").attr("type", "button").addClass("btn").addClass(confirmClass).attr("data-dismiss", "modal").append(confirmLabel);
    if (confirmCallback != null) {
        btnConfirm.click(function(ev) {
            invoke(confirmCallback, [ev]);
        });
    }
    openModal({
        backdrop: "static",
        title: title,
        content: msg,
        buttons: [btnCancel, btnConfirm]
    });
}

function randomUpperChar() {
    //[65,90]
    return String.fromCharCode(65 + Math.floor(Math.random() * 26));
}
function randomLowerChar() {
    //[97,122]
    return String.fromCharCode(97 + Math.floor(Math.random() * 26));
}
function randomDigit() {
    //[48,57]
    return String.fromCharCode(48 + Math.floor(Math.random() * 10));
}
function randomVisibleChar() {
    //[32,126]
    return String.fromCharCode(32 + Math.floor(Math.random() * 95));
}
var DICT_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
function randomString(len) {
    var str = "";
    for (var i = 0; i < len; i++) {
        str += DICT_CHARS.charAt(Math.floor(Math.random() * DICT_CHARS.length));
    }
    return str;
}
function randomVisibleString(len) {
    var str = "";
    while(str.length < len) {
        str += randomVisibleChar();
    }
    return str;
}
function randomNumber(min, max) {
    return min + Math.floor(Math.random() * (max - min));
}
function randomBoolean() {
    return Math.floor(Math.random() * 100) % 2 == 0;
}

function pushAll(array1, array2) {
    if (array2.length > 0) {
        for (var i = 0; i < array2.length; i++) {
            array1.push(array2[i]);
        }
    }
}